package com.sh.diytwo;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @Author: sh
 * @Version 1.0
 * @Description: 自定义线程池实现类
 */
public class MyThreadPoolTwo {

    // 用于保存工作线程（自定义线程），保证线程数量控制在核心线程数以内
    final Set<MyThreadTwo> set = new HashSet<>();

    // 任务阻塞队列，用于存放待执行的任务
    private MyBlockQueueTwo<Runnable> blockQueueTwo;

    // 核心线程数：线程池中允许同时存在的最大线程数量
    int corePoolSize;

    // 从阻塞队列获取任务时的最大等待时间
    long timeOut;

    // 时间单位（例如：TimeUnit.SECONDS）
    TimeUnit timeUnit;

    // 自定义的拒绝策略（当队列满且线程数已达上限时使用）
    MyRejectPolicyTwo<Runnable> myRejectPolicyTwo;

    // 最大线程数（包含核心线程）
    int maximumPoolSize;


    /**
     * 构造函数，初始化线程池参数
     *
     * @param blockQueueTwo     任务队列
     * @param corePoolSize      核心线程数
     * @param timeOut           获取任务最大超时时间
     * @param timeUnit          时间单位
     * @param myRejectPolicyTwo 自定义拒绝策略
     */
    public MyThreadPoolTwo(MyBlockQueueTwo<Runnable> blockQueueTwo, int corePoolSize, int maximumPoolSize, long timeOut,
                           TimeUnit timeUnit, MyRejectPolicyTwo<Runnable> myRejectPolicyTwo) {
        this.blockQueueTwo = blockQueueTwo;
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.timeOut = timeOut;
        this.timeUnit = timeUnit;
        this.myRejectPolicyTwo = myRejectPolicyTwo;
    }

    /**
     * 向线程池提交任务进行执行
     *
     * @param runnable 需要执行的任务
     */
    public void execute(Runnable runnable) {
        // 使用同步块确保线程安全地操作线程集合
        synchronized (set) {
            // 1、如果当前线程数量小于核心线程数，则创建一个新线程来执行任务
            int currentThreads  = set.size();
            if (currentThreads < corePoolSize) {
                // 创建一个新的自定义线程，并传入任务
                MyThreadTwo myThreadTwo = new MyThreadTwo(runnable);
                // 将线程添加到线程集合中，表示正在工作的线程
                set.add(myThreadTwo);
                // 启动线程，开始执行任务
                myThreadTwo.start();
            }else {
                // 2、如果大于核心线程数，则将任务放入阻塞队列中等待执行
                boolean put = blockQueueTwo.offer(runnable, timeOut, timeUnit);
                if (put) return;

                // 3、如果队列放不下，就判断是否达到了最大线程数
                if (currentThreads < maximumPoolSize) {
                    // 创建非核心线程（超出核心但没到最大值）
                    MyThreadTwo myThreadTwo = new MyThreadTwo(runnable);
                    set.add(myThreadTwo);
                    myThreadTwo.start();
                } else {
                    // 4、如果队列已满，最大线程数也满了，将触发用户自定义的拒绝策略
                    myRejectPolicyTwo.reject(blockQueueTwo, runnable);
                }
            }


        }
    }


    /**
     * 自定义线程类，封装任务执行逻辑
     */
    public class MyThreadTwo extends Thread {
        // 当前线程要执行的任务
        private Runnable task;

        // 构造方法，接收一个任务
        public MyThreadTwo(Runnable runnable) {
            this.task = runnable; // 初始化任务
        }

        @Override
        public void run() {
            // 循环执行任务：只要当前任务不为空，或者从任务队列中取到了新的任务
            while (task != null || (task = blockQueueTwo.poll(timeOut, timeUnit)) != null) {
                try {
                    // 执行当前任务
                    task.run();
                } catch (Exception e) {
                    // 捕获异常，防止任务抛出异常导致线程终止
                    throw new RuntimeException(e);
                } finally {
                    // 任务执行完毕后，将 task 置为 null，准备下一轮取任务
                    task = null;
                }
            }

            // 当线程没有任务执行时（从队列中也取不到任务），将其从线程池中移除
            synchronized (set) {
                set.remove(this); // 线程自然结束后从集合中移除，释放资源
            }
        }
    }

}
